
#include <dlfcn.h>
#include "core/modules.h"
#include "config.h"

Module::Module(void)
    : mod_(NULL)
    , create_(NULL)
    , init_(NULL)
    , release_(NULL)
{}

void* Module::Create(void) {
  return create_ ? create_() : NULL;
}

int32_t Module::Init(void* service, const char* param) {
  return init_ ? init_(service, param) : 0;
}

void Module::Release(void* context) {
  if (release_)
    release_(context);
}

Module::~Module(void) {
  dlclose(mod_);
}

namespace {
// mutex for dlopen, dlerror is not thread-safe
boost::mutex g_dl_mutex; 
void* LoadModule(const std::string& name) {
  std::string mpath = Config::Get("server.module_path");

  for (std::string::size_type i = 0; i < mpath.size(); ++i) {
    std::string path;
    for (; i < mpath.size(); ++i) {
      char c = mpath[i];
      if (c == '?')
        path.append(name);
      else if(c == ':')
        break;
      else
        path.push_back(c);
    }

    boost::mutex::scoped_lock l(g_dl_mutex);
    void* mod = dlopen(path.c_str(), RTLD_NOW);
    if (mod) 
      return mod;
    else {
      char* err = dlerror();
      DLOG(INFO) << err;
    }
  }
  return NULL;
}
}

ModulePtr Modules::Query(const std::string& name) {
  boost::mutex::scoped_lock l(mutex_);
  auto it = std::find_if(modules_.begin(), modules_.end(),
                         [&name](const boost::weak_ptr<Module>& m) { 
                           auto mptr = m.lock();
                           return mptr && mptr->name_ == name;
                         });
  if (it != modules_.end()) {
    ModulePtr m((*it).lock());
    if (m) return m;
    else modules_.erase(it); 
  }

  void* mod = LoadModule(name);
  if (!mod) {
    return ModulePtr();
  }

  ModulePtr m(new Module);
  m->name_ = name;
  m->mod_ = mod;

  m->init_ = (ModuleInit)dlsym(m->mod_, "Init");
  m->create_ = (ModuleCreate)dlsym(m->mod_, "Create");
  m->release_ = (ModuleRelease)dlsym(m->mod_, "Release");

  modules_.push_back(m);
  return m;
}


